package com.aisi.config;

import com.aisi.entity.RestBean;
import com.aisi.entity.dto.Account;
import com.aisi.entity.vo.response.AuthorizeVO;
import com.aisi.filter.JwtAuthorizeFilter;
import com.aisi.service.AccountService;
import com.aisi.utils.JwtUtils;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.io.IOException;
import java.io.PrintWriter;

/**
 * @Author: shenjianZ
 * @Date: 2024/2/14 12:43
 * @Description: Spring Security的配置类，设置安全相关的配置。
 *               定义HTTP请求的访问规则，添加过滤器链来处理这些请求。
 */

@Configuration
public class SecurityConfiguration {

    @Resource
    private JwtUtils utils;
    @Resource
    private JwtAuthorizeFilter jwtAuthorizeFilter;

    @Resource
    private AccountService accountService;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity
                .authorizeHttpRequests(conf -> conf
                        .requestMatchers("/api/auth/**" ,"/error").permitAll()
                        .anyRequest().authenticated()

                )
                .formLogin(conf -> conf
                        .loginProcessingUrl("/api/auth/login")
                        .failureHandler(this::onAuthenticationFailure) // 认证失败
                        .successHandler(this::onAuthenticationSuccess)) // 认证成功
                .logout(conf -> conf
                        .logoutUrl("/api/auth/logout")
                        .logoutSuccessHandler(this::onLogoutSuccess))
                .exceptionHandling(conf -> conf
                        .authenticationEntryPoint(this::onUnauthorized) //未登录验证处理
                        .accessDeniedHandler(this::onAccessDeny))  //登录验证通过但权限不足
                .csrf(AbstractHttpConfigurer::disable)
                .sessionManagement(conf -> conf
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .addFilterBefore(jwtAuthorizeFilter, UsernamePasswordAuthenticationFilter.class)
                .build();

    }


    private void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
        response.setContentType("application/json;charset=utf-8");
        String authorization = request.getHeader("Authorization");
        PrintWriter writer = response.getWriter();
        if (utils.invalidateJwt(authorization)){
            writer.write(RestBean.success().asJsonString());
        }else{
            writer.write(RestBean.failure(400,"退出登录失败").asJsonString());
        }
    }


    private void onAuthenticationSuccess(HttpServletRequest request,
                                         HttpServletResponse response,
                                         Authentication authentication) throws IOException {
        response.setContentType("application/json;charset=utf-8");
        User user = (User) authentication.getPrincipal();
        Account account = accountService.findAccountByUsernameOrEmail(user.getUsername());
        String token = utils.createJwt(user, account.getId(), account.getUsername());
//        AuthorizeVO authorizeVO = new AuthorizeVO();
//        BeanUtils.copyProperties(account,authorizeVO); // bean拷贝
        AuthorizeVO authorizeVO=account.asViewObject(AuthorizeVO.class, vo ->{
            vo.setToken(token);
            vo.setExpire(utils.expireTime());
        });

        response.getWriter().write(RestBean.success(authorizeVO).asJsonString());
    }

    private void onAuthenticationFailure(HttpServletRequest request,
                                         HttpServletResponse response,
                                         AuthenticationException e) throws IOException {
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(RestBean.unauthorized(e.getMessage()).asJsonString());
    }

    private void onAccessDeny(HttpServletRequest request,
                              HttpServletResponse response,
                              AccessDeniedException e) throws IOException {
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(RestBean.forbidden(e.getMessage()).asJsonString());
    }

    private void onUnauthorized(HttpServletRequest request,
                                HttpServletResponse response,
                                AuthenticationException e) throws IOException {
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(RestBean.unauthorized(e.getMessage()).asJsonString());
    }
}
